package com.webull.information.center.carwler.task.support;

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jsoup.nodes.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.webull.cms.client.model.YgTickerMap;
import com.webull.cms.client.service.YgTickerMapService;
import com.webull.information.center.carwler.common.model.BranchNews;
import com.webull.information.center.carwler.common.model.NewsInformation;
import com.webull.information.center.carwler.common.model.StockConfig;
import com.webull.information.center.carwler.common.util.BranchNewsUtil;
import com.webull.information.center.carwler.common.util.CarwlerJobStateUtil;
import com.webull.information.center.carwler.common.util.jsoup.HttpJsoupClient;
import com.webull.information.center.carwler.common.util.news.NewsHtmlPraseMapper;
import com.webull.information.center.carwler.common.util.news.NewsWrapperUtil;
import com.webull.information.center.carwler.service.CarwlerNewsService;
import com.webull.information.center.carwler.task.handler.StockReuteGuideHandler;
import com.webull.information.center.carwler.task.handler.WallstreetCnNewsHandler;
import com.webull.information.client.model.News;
import com.webull.information.client.model.parm.NewsRelType;
import com.webull.securities.client.model.ExchangeMapping;
import com.webull.securities.client.model.parm.ExchangeMappingParam;
import com.webull.securities.client.service.ExchangeService;

/**
 * 新闻任务处理器
 *
 * @author shimingjun
 * @version 1.0
 * @date 2016年8月12日 下午4:00:41
 * @since JDK 1.8
 */
@Component
public class TikcerNewsProcessor {
    protected final Logger logger = LogManager.getLogger(getClass());

    private @Autowired
    NewsHtmlPraseMapper newsHtmlPraseMapper;
    private @Autowired
    NewsWrapperUtil newsWrapperUtil;
    private @Autowired
    StockReuteGuideHandler stockReuteGuideHandler;
    private @Autowired
    HttpJsoupClient htmlPrase;
    private @Autowired
    ExchangeService exchangeService;
    private @Autowired
    YgTickerMapService ygTickerMapService;
    private @Autowired
    NewsProcessorSupport newsProcessorSupport;
    private @Autowired
    CarwlerJobStateUtil carwlerJobStateUtil;
    private @Autowired
    CarwlerNewsService carwlerNewsService;
	private @Autowired WallstreetCnNewsHandler wallstreetCnNewsHandler;


    private String genAndSetUrlMd5(NewsInformation info) {
        final String urlMd5 = newsWrapperUtil.isSpecCollect(info.getCollectSource()) //
                ? DigestUtils.sha1Hex(info.getUrl()) //
                : HttpJsoupClient.finishingMd5Url(info.getUrl());
        info.setUrlMd5(urlMd5);
        return urlMd5;
    }

    /**
     * /**
     * 解析info并保存新闻记录
     *
     * @param info     info对象
     * @param targetId 标的物id
     * @param relType  关联类型
     */
    public void bindTargetNewsRecord(final NewsInformation info, final Integer targetId, final NewsRelType relType) {
        bindTargetNewsRecord2(info, targetId, null, relType);
    }

    /**
     * @param info
     * @param targetId
     * @param targetMd5 要关联的新闻md5
     * @param relType
     */
    private void bindTargetNewsRecord2(final NewsInformation info, final Integer targetId, final String targetMd5, final NewsRelType relType) {
		if (info == null || StringUtils.isBlank(info.getUrl())) {
            return;
        }
        String link = info.getUrl();
        final String urlMd5 = genAndSetUrlMd5(info);
        if (StringUtils.isBlank(urlMd5)) return;

        News dbNews = newsProcessorSupport.getByUrlMd5(urlMd5);

        /*
            step1  判断base库是否存在相同新闻
            isMain, 增加关联关系后return
            isBranch 增加至branch的表后return
         */

		if (dbNews != null 
				&& !newsWrapperUtil.needToUpdate2(info, dbNews)) {// 不需要更新
			logger.info("false--->whetherUpdate:" + link + ";md5:" + urlMd5);
            info.setNewsId(dbNews.getId());
			if (!NewsRelType.homePage.equals(relType) && targetId != null) {
                newsProcessorSupport.addNewsInfoRel(targetId, relType, dbNews.getId().intValue());
            }
			/**
			 * 处理关联的新闻(如果存在)
			 */
			processNewsAssociates(info);
			return;
        }
        /**
         * associate 类型文件暂不执行单独的采集逻辑，这需要注意html的ver控制问题
         * 新增rel即可
         */

		final Consumer<?> processNewsContext = o -> {
            parseNewsDoc(link, info);
            /**
             * 正文不为空才插入
             */
            if (StringUtils.isNotBlank(info.getContent())) {
                newsProcessorSupport.productionNewsInfoRecord(targetId, targetMd5, relType, info);
            }
        };
        if (carwlerJobStateUtil.isCarwlerBase()) {
            /**
             * 替换标题相同的新闻内容
             */
			if (dbNews == null) {
				dbNews = Optional
						.ofNullable(newsProcessorSupport.getBySumMd5(info.getSumMd5(), relType, targetId,
								info.getNewsTime()))//
						.filter(news_ -> Optional.ofNullable(info.getSiteType()).orElse(News.SiteType.NORMAL)
								.equals(news_.getSiteType()))
                        .orElse(null);
            }
            Date nesTime = Optional.ofNullable(dbNews).map(n -> n.getNewsTime()).orElse(null);
            Optional.ofNullable(dbNews).ifPresent(dbNews_ -> {
                info.setNewsId(dbNews_.getId());
                info.setStoreShard(dbNews_.getStoreShard());
            });

            if (nesTime == null//
					|| info.getNewsTime().after(nesTime)
					|| News.HomeType.frontPage.equals(info.getHomePage())) {
                processNewsContext.accept(null);
            }

        } else {
            List<BranchNews> branchNews = carwlerNewsService.searchBranchNewsByMd5(urlMd5);

			Optional.ofNullable(carwlerNewsService.searchBranchNewsBySumMd5(info.getSumMd5()))//
					.filter(vv -> !vv.isEmpty())//
					.ifPresent(vv -> branchNews.addAll(vv));

            /*
			 * 需要添加branchNews
			 */
            final Predicate<BranchNews> whetherSameBranch = bns -> {
				// if (!Objects.equals(info.getUrlMd5(), bns.getUrlMd5())) return false;
                if (NewsRelType.homePage.equals(relType) && !Objects.equals(info.getHomePage(), bns.getHomePage()))
                    return false;
				if (!NewsRelType.homePage.equals(relType)//
						&& (!Objects.equals(targetId, bns.getTargetId())
								|| !Objects.equals(BranchNewsUtil.genBranchTargetType(relType), bns.getTargetType()))
				)
					return false;
                return true;
            };
            if (branchNews == null || branchNews.isEmpty()) {
                processNewsContext.accept(null);
            } else if (branchNews.stream().noneMatch(whetherSameBranch)) {
                /*
				 * 清除状态
				 */
                BranchNews bnews = branchNews.get(0);//取有用的附属信息，用来生成流水记录
                bnews.setId(null);
                bnews.setTargetId(null);
                bnews.setTargetType(null);
                bnews.setTargetMd5(null);


                carwlerNewsService.addBranchNewsRecord2(targetId, targetMd5, relType, bnews);
            }
        }
        /**
         * 处理关联的新闻(如果存在)
         */
		processNewsAssociates(info);

    }

	private void processNewsAssociates(NewsInformation info) {
		Optional.ofNullable(info.getAssociates()).ifPresent(v0 -> v0.forEach(v -> processNewsAssociates(v)));
	}

    public Long bindBranchNews(BranchNews bnews) {
        carwlerJobStateUtil.assertCarwlerJobConfig(true);

        // 查md5
        // 没查到 ? 标题重复 insert/update news+rel、
        // 查到了 ?  inset rel
        final String urlMd5 = bnews.getUrlMd5();
        NewsRelType relType = BranchNewsUtil.genNewsRelType(bnews.getTargetType());

        /**
         * 补齐targetId
         */
        if (NewsRelType.associate.equals(relType) && bnews.getTargetId() == null) {
            Optional.ofNullable(newsProcessorSupport.getByUrlMd5(bnews.getTargetMd5()))
                    .ifPresent(v -> bnews.setTargetId(v.getId().intValue()));
            if (bnews.getTargetId() == null) return null;
        }

        News dbNews = newsProcessorSupport.getByUrlMd5(urlMd5);
        if (dbNews == null) {
            // 有可能标题重复的
			dbNews = newsProcessorSupport.getBySumMd5(bnews.getSumMd5(), relType, bnews.getTargetId(),
					bnews.getNewsTime());
            if (dbNews == null) {
                // insert
                return newsProcessorSupport.bindBranchNewsAndRel(bnews, null);
            } else {
				if (ObjectUtils.compare(bnews.getNewsTime(), dbNews.getNewsTime()) > 0
						|| ObjectUtils.compare(bnews.getHomePage(), dbNews.getHomePage()) > 0) {
                    // update
                    return newsProcessorSupport.bindBranchNewsAndRel(bnews, dbNews.getId());
                }
            }
        } else {
            if (!NewsRelType.homePage.equals(relType)) {// 不是homePage
                //insert  baserel
                newsProcessorSupport.addNewsInfoRel(bnews.getTargetId(), relType, dbNews.getId().intValue());
                return dbNews.getId();
            }
        }

        return null;
    }

    /**
     * 处理关联的新闻
     *
     * @param assoc
     */
    private void processNewsAssociates(NewsInformation.AssociatesNews assoc) {
        if (assoc == null) return;

        String s = genAndSetUrlMd5(assoc);//生成urlMd5
        if (StringUtils.isBlank(s)) return;

        Integer assNewsId0 = Optional.ofNullable(assoc.getAssNewsId()).map(v -> v.intValue())
                .orElseGet(() -> {
                    if (StringUtils.isNotBlank(assoc.getAsslMd5())) {
                        return Optional.ofNullable(newsProcessorSupport.getByUrlMd5(assoc.getAsslMd5()))
                                .map(v -> v.getId().intValue()).orElse(null);
                    }
                    return null;
                });


        if (assNewsId0 != null || StringUtils.isNotBlank(assoc.getAsslMd5()))
            bindTargetNewsRecord2(assoc, assNewsId0,
                    assoc.getAsslMd5(), NewsRelType.associate);
    }


    /**
     * @param news
     * @since 0.1.0
     */
    public void updateNews4S3(News news) {

		// boolean needUpdate = newsWrapperUtil.whetherUpdate(news);
		// if (!needUpdate) {// 不 需要更新2
		// return;
		// }
		// NewsInformation info = new NewsInformation();
		// BeanUtils.copyProperties(news, info);
		//
		// String link = info.getUrl();
		// // info.setUrlMd5(HtmlPrase.finishingMd5Url(link));// 重新生成md5
		// parseNewsDoc(link, info);
		// if (StringUtils.isNotBlank(info.getContent())) {
		// newsProcessorSupport.updateNewsS3(news, info);
		// }
    }

    /**
     * 解析网页并填充InitialInformation对象
     *
     * @param link
     * @param info
     * @since 0.1.0
     */
    private void parseNewsDoc(String link, NewsInformation info) {
        try {
            if (News.SiteType.PIRORIT.equals(info.getSiteType())) {// 聚合网站不需要解析网页本身
                info.setContent("content"); // 用于标示
                return;
            } else if (newsWrapperUtil.isSpecCollect(info.getCollectSource())) {
                // 恒生新闻
                //路透新闻
                if (News.CollectSource.reutersGuide.equals(info.getCollectSource())) {
                    if (News.HomeType.top.equals(info.getHomePage())
                            || News.HomeType.frontPage.equals(info.getHomePage())) {
                        //topNews解析
                        stockReuteGuideHandler.praseReutersTopNewsInfo(info);
                    } else {//普通标的新闻解析
                        stockReuteGuideHandler.praseReutersNewsInfo(info);
                    }
				} else if (News.CollectSource.wallStreet.equals(info.getCollectSource())) {
					/*
					 * 本身就是被关联新闻，就不再寻找关联新闻了
					 */
					boolean addAssoc = !NewsInformation.AssociatesNews.class.equals(info.getClass());

					wallstreetCnNewsHandler.processTextReturnAssoc(info, addAssoc);
                }
                return;


            }
            Optional.ofNullable(newsHtmlPraseMapper.findHtmlBodyPrase(link)).ifPresent(assembly0 -> {
                Boolean proxy = newsHtmlPraseMapper.getPraseProxyMode(link);
                Document doc = htmlPrase.parseDocumentFromUrl(link, proxy);
                if (doc != null) {


                    assembly0.praseNewsInfo(doc, info);
                    if (StringUtils.isBlank(info.getContent())) {
                        logger.error("parseNewsDoc context is null .error:link:{},parse:{}", link,
                                assembly0.getClass().getSimpleName());
                    }
                }
            });
        } catch (Exception e) {
            logger.error("assemblyNewsDoc error:{}", e);
        }
    }

    /**
     * 特殊的源，不需要解析和通过url生成md5
     */
    // public static void main(String[] args) {
    // String a = "2312312d中华華dff3";
    //
    // boolean b =
    // a.equalsIgnoreCase(ChineseHelper.convertToSimplifiedChinese(a));
    // System.out.println(b);
    //
    // }
    public String googleExchage(Integer wbEXid, String wbExCode) {
        if (wbEXid == null)
            return null;
        ExchangeMappingParam param = new ExchangeMappingParam();
        param.setExchangeId(wbEXid);
        param.setSourceType(ExchangeMapping.SourceTypeConstants.GOOGLE);

        ExchangeMapping mapping = exchangeService.searchExchangeMapping(param);

        String googl_exchange = Optional.ofNullable(mapping).map(mappi -> mappi.getExchangeCode()).orElse(wbExCode);
        return googl_exchange;
    }

    public String yahooExchage(Integer wbEXid, String wbExCode) {
        if (wbEXid == null)
            return null;
        ExchangeMappingParam param = new ExchangeMappingParam();
        param.setExchangeId(wbEXid);
        param.setSourceType(ExchangeMapping.SourceTypeConstants.YAHOO);

        ExchangeMapping mapping = exchangeService.searchExchangeMapping(param);

        String googl_exchange = Optional.ofNullable(mapping).map(mappi -> mappi.getExchangeCode()).orElse(wbExCode);
        return googl_exchange;
    }

    public String googleSymbol(Integer wbStockid, String wbSymol) {
        YgTickerMap param = new YgTickerMap();
        param.setTickerId(wbStockid);
        return Optional.ofNullable(ygTickerMapService.search(param))//
                .filter(v -> v.size() >= 1).map(v -> v.get(0).getSymbol()).orElse(wbSymol);
    }


    public void updateStockConfig(StockConfig stockConfig, List<Date> times) {
        Collections.sort(times, (d1, d2) -> d1.after(d2) ? -1 : 1);
        Date last = times.get(0);
        stockConfig.setCollectTime(last);
        stockConfig.setCollectNumber(times.size());
    }
}
